home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkCanvLine.c < prev    next >
C/C++ Source or Header  |  1995-06-09  |  49KB  |  1,620 lines

  1. /* 
  2.  * tkCanvLine.c --
  3.  *
  4.  *    This file implements line items for canvas widgets.
  5.  *
  6.  * Copyright (c) 1991-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  */
  12.  
  13. static char sccsid[] = "@(#) tkCanvLine.c 1.35 95/06/09 09:46:54";
  14.  
  15. #include <stdio.h>
  16. #include "tkInt.h"
  17. #include "tkPort.h"
  18.  
  19. /*
  20.  * The structure below defines the record for each line item.
  21.  */
  22.  
  23. typedef struct LineItem  {
  24.     Tk_Item header;        /* Generic stuff that's the same for all
  25.                  * types.  MUST BE FIRST IN STRUCTURE. */
  26.     Tk_Canvas canvas;        /* Canvas containing item.  Needed for
  27.                  * parsing arrow shapes. */
  28.     int numPoints;        /* Number of points in line (always >= 2). */
  29.     double *coordPtr;        /* Pointer to malloc-ed array containing
  30.                  * x- and y-coords of all points in line.
  31.                  * X-coords are even-valued indices, y-coords
  32.                  * are corresponding odd-valued indices. If
  33.                  * the line has arrowheads then the first
  34.                  * and last points have been adjusted to refer
  35.                  * to the necks of the arrowheads rather than
  36.                  * their tips.  The actual endpoints are
  37.                  * stored in the *firstArrowPtr and
  38.                  * *lastArrowPtr, if they exist. */
  39.     int width;            /* Width of line. */
  40.     XColor *fg;            /* Foreground color for line. */
  41.     Pixmap fillStipple;        /* Stipple bitmap for filling line. */
  42.     int capStyle;        /* Cap style for line. */
  43.     int joinStyle;        /* Join style for line. */
  44.     GC gc;            /* Graphics context for filling line. */
  45.     GC arrowGC;            /* Graphics context for drawing arrowheads. */
  46.     Tk_Uid arrow;        /* Indicates whether or not to draw arrowheads:
  47.                  * "none", "first", "last", or "both". */
  48.     float arrowShapeA;        /* Distance from tip of arrowhead to center. */
  49.     float arrowShapeB;        /* Distance from tip of arrowhead to trailing
  50.                  * point, measured along shaft. */
  51.     float arrowShapeC;        /* Distance of trailing points from outside
  52.                  * edge of shaft. */
  53.     double *firstArrowPtr;    /* Points to array of PTS_IN_ARROW points
  54.                  * describing polygon for arrowhead at first
  55.                  * point in line.  First point of arrowhead
  56.                  * is tip.  Malloc'ed.  NULL means no arrowhead
  57.                  * at first point. */
  58.     double *lastArrowPtr;    /* Points to polygon for arrowhead at last
  59.                  * point in line (PTS_IN_ARROW points, first
  60.                  * of which is tip).  Malloc'ed.  NULL means
  61.                  * no arrowhead at last point. */
  62.     int smooth;            /* Non-zero means draw line smoothed (i.e.
  63.                  * with Bezier splines). */
  64.     int splineSteps;        /* Number of steps in each spline segment. */
  65. } LineItem;
  66.  
  67. /*
  68.  * Number of points in an arrowHead:
  69.  */
  70.  
  71. #define PTS_IN_ARROW 6
  72.  
  73. /*
  74.  * Prototypes for procedures defined in this file:
  75.  */
  76.  
  77. static int        ArrowheadPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  78.                 Tk_Canvas canvas, LineItem *linePtr,
  79.                 double *arrowPtr));
  80. static void        ComputeLineBbox _ANSI_ARGS_((Tk_Canvas canvas,
  81.                 LineItem *linePtr));
  82. static int        ConfigureLine _ANSI_ARGS_((Tcl_Interp *interp,
  83.                 Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
  84.                 char **argv, int flags));
  85. static int        ConfigureArrows _ANSI_ARGS_((Tk_Canvas canvas,
  86.                 LineItem *linePtr));
  87. static int        CreateLine _ANSI_ARGS_((Tcl_Interp *interp,
  88.                 Tk_Canvas canvas, struct Tk_Item *itemPtr,
  89.                 int argc, char **argv));
  90. static void        DeleteLine _ANSI_ARGS_((Tk_Canvas canvas,
  91.                 Tk_Item *itemPtr, Display *display));
  92. static void        DisplayLine _ANSI_ARGS_((Tk_Canvas canvas,
  93.                 Tk_Item *itemPtr, Display *display, Drawable dst,
  94.                 int x, int y, int width, int height));
  95. static int        LineCoords _ANSI_ARGS_((Tcl_Interp *interp,
  96.                 Tk_Canvas canvas, Tk_Item *itemPtr,
  97.                 int argc, char **argv));
  98. static int        LineToArea _ANSI_ARGS_((Tk_Canvas canvas,
  99.                 Tk_Item *itemPtr, double *rectPtr));
  100. static double        LineToPoint _ANSI_ARGS_((Tk_Canvas canvas,
  101.                 Tk_Item *itemPtr, double *coordPtr));
  102. static int        LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  103.                 Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
  104. static int        ParseArrowShape _ANSI_ARGS_((ClientData clientData,
  105.                 Tcl_Interp *interp, Tk_Window tkwin, char *value,
  106.                 char *recordPtr, int offset));
  107. static char *        PrintArrowShape _ANSI_ARGS_((ClientData clientData,
  108.                 Tk_Window tkwin, char *recordPtr, int offset,
  109.                 Tcl_FreeProc **freeProcPtr));
  110. static void        ScaleLine _ANSI_ARGS_((Tk_Canvas canvas,
  111.                 Tk_Item *itemPtr, double originX, double originY,
  112.                 double scaleX, double scaleY));
  113. static void        TranslateLine _ANSI_ARGS_((Tk_Canvas canvas,
  114.                 Tk_Item *itemPtr, double deltaX, double deltaY));
  115.  
  116. /*
  117.  * Information used for parsing configuration specs.  If you change any
  118.  * of the default strings, be sure to change the corresponding default
  119.  * values in CreateLine.
  120.  */
  121.  
  122. static Tk_CustomOption arrowShapeOption = {ParseArrowShape,
  123.     PrintArrowShape, (ClientData) NULL};
  124.  
  125. static Tk_ConfigSpec configSpecs[] = {
  126.     {TK_CONFIG_UID, "-arrow", (char *) NULL, (char *) NULL,
  127.     "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT},
  128.     {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL,
  129.     "8 10 3", Tk_Offset(LineItem, arrowShapeA),
  130.     TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},
  131.     {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL,
  132.     "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT},
  133.     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
  134.     "black", Tk_Offset(LineItem, fg), TK_CONFIG_NULL_OK},
  135.     {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,
  136.     "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},
  137.     {TK_CONFIG_BOOLEAN, "-smooth", (char *) NULL, (char *) NULL,
  138.     "0", Tk_Offset(LineItem, smooth), TK_CONFIG_DONT_SET_DEFAULT},
  139.     {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,
  140.     "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},
  141.     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
  142.     (char *) NULL, Tk_Offset(LineItem, fillStipple), TK_CONFIG_NULL_OK},
  143.     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
  144.     (char *) NULL, 0, TK_CONFIG_NULL_OK, &tk_CanvasTagsOption},
  145.     {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
  146.     "1", Tk_Offset(LineItem, width), TK_CONFIG_DONT_SET_DEFAULT},
  147.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  148.     (char *) NULL, 0, 0}
  149. };
  150.  
  151. /*
  152.  * The structures below defines the line item type by means
  153.  * of procedures that can be invoked by generic item code.
  154.  */
  155.  
  156. Tk_ItemType tkLineType = {
  157.     "line",                /* name */
  158.     sizeof(LineItem),            /* itemSize */
  159.     CreateLine,                /* createProc */
  160.     configSpecs,            /* configSpecs */
  161.     ConfigureLine,            /* configureProc */
  162.     LineCoords,                /* coordProc */
  163.     DeleteLine,                /* deleteProc */
  164.     DisplayLine,            /* displayProc */
  165.     0,                    /* alwaysRedraw */
  166.     LineToPoint,            /* pointProc */
  167.     LineToArea,                /* areaProc */
  168.     LineToPostscript,            /* postscriptProc */
  169.     ScaleLine,                /* scaleProc */
  170.     TranslateLine,            /* translateProc */
  171.     (Tk_ItemIndexProc *) NULL,        /* indexProc */
  172.     (Tk_ItemCursorProc *) NULL,        /* icursorProc */
  173.     (Tk_ItemSelectionProc *) NULL,    /* selectionProc */
  174.     (Tk_ItemInsertProc *) NULL,        /* insertProc */
  175.     (Tk_ItemDCharsProc *) NULL,        /* dTextProc */
  176.     (Tk_ItemType *) NULL        /* nextPtr */
  177. };
  178.  
  179. /*
  180.  * The Tk_Uid's below refer to uids for the various arrow types:
  181.  */
  182.  
  183. static Tk_Uid noneUid = NULL;
  184. static Tk_Uid firstUid = NULL;
  185. static Tk_Uid lastUid = NULL;
  186. static Tk_Uid bothUid = NULL;
  187.  
  188. /*
  189.  * The definition below determines how large are static arrays
  190.  * used to hold spline points (splines larger than this have to
  191.  * have their arrays malloc-ed).
  192.  */
  193.  
  194. #define MAX_STATIC_POINTS 200
  195.  
  196. /*
  197.  *--------------------------------------------------------------
  198.  *
  199.  * CreateLine --
  200.  *
  201.  *    This procedure is invoked to create a new line item in
  202.  *    a canvas.
  203.  *
  204.  * Results:
  205.  *    A standard Tcl return value.  If an error occurred in
  206.  *    creating the item, then an error message is left in
  207.  *    interp->result;  in this case itemPtr is left uninitialized,
  208.  *    so it can be safely freed by the caller.
  209.  *
  210.  * Side effects:
  211.  *    A new line item is created.
  212.  *
  213.  *--------------------------------------------------------------
  214.  */
  215.  
  216. static int
  217. CreateLine(interp, canvas, itemPtr, argc, argv)
  218.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  219.     Tk_Canvas canvas;            /* Canvas to hold new item. */
  220.     Tk_Item *itemPtr;            /* Record to hold new item;  header
  221.                      * has been initialized by caller. */
  222.     int argc;                /* Number of arguments in argv. */
  223.     char **argv;            /* Arguments describing line. */
  224. {
  225.     LineItem *linePtr = (LineItem *) itemPtr;
  226.     int i;
  227.  
  228.     if (argc < 4) {
  229.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  230.         Tk_PathName(Tk_CanvasTkwin(canvas)), "\" create ",
  231.         itemPtr->typePtr->name, " x1 y1 x2 y2 ?x3 y3 ...? ?options?",
  232.         (char *) NULL);
  233.     return TCL_ERROR;
  234.     }
  235.  
  236.     /*
  237.      * Carry out initialization that is needed to set defaults and to
  238.      * allow proper cleanup after errors during the the remainder of
  239.      * this procedure.
  240.      */
  241.  
  242.     linePtr->canvas = canvas;
  243.     linePtr->numPoints = 0;
  244.     linePtr->coordPtr = NULL;
  245.     linePtr->width = 1;
  246.     linePtr->fg = None;
  247.     linePtr->fillStipple = None;
  248.     linePtr->capStyle = CapButt;
  249.     linePtr->joinStyle = JoinRound;
  250.     linePtr->gc = None;
  251.     linePtr->arrowGC = None;
  252.     if (noneUid == NULL) {
  253.     noneUid = Tk_GetUid("none");
  254.     firstUid = Tk_GetUid("first");
  255.     lastUid = Tk_GetUid("last");
  256.     bothUid = Tk_GetUid("both");
  257.     }
  258.     linePtr->arrow = noneUid;
  259.     linePtr->arrowShapeA = 8.0;
  260.     linePtr->arrowShapeB = 10.0;
  261.     linePtr->arrowShapeC = 3.0;
  262.     linePtr->firstArrowPtr = NULL;
  263.     linePtr->lastArrowPtr = NULL;
  264.     linePtr->smooth = 0;
  265.     linePtr->splineSteps = 12;
  266.  
  267.     /*
  268.      * Count the number of points and then parse them into a point
  269.      * array.  Leading arguments are assumed to be points if they
  270.      * start with a digit or a minus sign followed by a digit.
  271.      */
  272.  
  273.     for (i = 4; i < (argc-1); i+=2) {
  274.     if ((!isdigit(UCHAR(argv[i][0]))) &&
  275.         ((argv[i][0] != '-')
  276.         || ((argv[i][1] != '.') && !isdigit(UCHAR(argv[i][1]))))) {
  277.         break;
  278.     }
  279.     }
  280.     if (LineCoords(interp, canvas, itemPtr, i, argv) != TCL_OK) {
  281.     goto error;
  282.     }
  283.     if (ConfigureLine(interp, canvas, itemPtr, argc-i, argv+i, 0) == TCL_OK) {
  284.     return TCL_OK;
  285.     }
  286.  
  287.     error:
  288.     DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
  289.     return TCL_ERROR;
  290. }
  291.  
  292. /*
  293.  *--------------------------------------------------------------
  294.  *
  295.  * LineCoords --
  296.  *
  297.  *    This procedure is invoked to process the "coords" widget
  298.  *    command on lines.  See the user documentation for details
  299.  *    on what it does.
  300.  *
  301.  * Results:
  302.  *    Returns TCL_OK or TCL_ERROR, and sets interp->result.
  303.  *
  304.  * Side effects:
  305.  *    The coordinates for the given item may be changed.
  306.  *
  307.  *--------------------------------------------------------------
  308.  */
  309.  
  310. static int
  311. LineCoords(interp, canvas, itemPtr, argc, argv)
  312.     Tcl_Interp *interp;            /* Used for error reporting. */
  313.     Tk_Canvas canvas;            /* Canvas containing item. */
  314.     Tk_Item *itemPtr;            /* Item whose coordinates are to be
  315.                      * read or modified. */
  316.     int argc;                /* Number of coordinates supplied in
  317.                      * argv. */
  318.     char **argv;            /* Array of coordinates: x1, y1,
  319.                      * x2, y2, ... */
  320. {
  321.     LineItem *linePtr = (LineItem *) itemPtr;
  322.     char buffer[TCL_DOUBLE_SPACE];
  323.     int i, numPoints;
  324.  
  325.     if (argc == 0) {
  326.     double *coordPtr;
  327.     int numCoords;
  328.  
  329.     numCoords = 2*linePtr->numPoints;
  330.     if (linePtr->firstArrowPtr != NULL) {
  331.         coordPtr = linePtr->firstArrowPtr;
  332.     } else {
  333.         coordPtr = linePtr->coordPtr;
  334.     }
  335.     for (i = 0; i < numCoords; i++, coordPtr++) {
  336.         if (i == 2) {
  337.         coordPtr = linePtr->coordPtr+2;
  338.         }
  339.         if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) {
  340.         coordPtr = linePtr->lastArrowPtr;
  341.         }
  342.         Tcl_PrintDouble(interp, *coordPtr, buffer);
  343.         Tcl_AppendElement(interp, buffer);
  344.     }
  345.     } else if (argc < 4) {
  346.     Tcl_AppendResult(interp,
  347.         "too few coordinates for line:  must have at least 4",
  348.         (char *) NULL);
  349.     return TCL_ERROR;
  350.     } else if (argc & 1) {
  351.     Tcl_AppendResult(interp,
  352.         "odd number of coordinates specified for line",
  353.         (char *) NULL);
  354.     return TCL_ERROR;
  355.     } else {
  356.     numPoints = argc/2;
  357.     if (linePtr->numPoints != numPoints) {
  358.         if (linePtr->coordPtr != NULL) {
  359.         ckfree((char *) linePtr->coordPtr);
  360.         }
  361.         linePtr->coordPtr = (double *) ckalloc((unsigned)
  362.             (sizeof(double) * argc));
  363.         linePtr->numPoints = numPoints;
  364.     }
  365.     for (i = argc-1; i >= 0; i--) {
  366.         if (Tk_CanvasGetCoord(interp, canvas, argv[i],
  367.             &linePtr->coordPtr[i]) != TCL_OK) {
  368.         return TCL_ERROR;
  369.         }
  370.     }
  371.  
  372.     /*
  373.      * Update arrowheads by throwing away any existing arrow-head
  374.      * information and calling ConfigureArrows to recompute it.
  375.      */
  376.  
  377.     if (linePtr->firstArrowPtr != NULL) {
  378.         ckfree((char *) linePtr->firstArrowPtr);
  379.         linePtr->firstArrowPtr = NULL;
  380.     }
  381.     if (linePtr->lastArrowPtr != NULL) {
  382.         ckfree((char *) linePtr->lastArrowPtr);
  383.         linePtr->lastArrowPtr = NULL;
  384.     }
  385.     if (linePtr->arrow != noneUid) {
  386.         ConfigureArrows(canvas, linePtr);
  387.     }
  388.     ComputeLineBbox(canvas, linePtr);
  389.     }
  390.     return TCL_OK;
  391. }
  392.  
  393. /*
  394.  *--------------------------------------------------------------
  395.  *
  396.  * ConfigureLine --
  397.  *
  398.  *    This procedure is invoked to configure various aspects
  399.  *    of a line item such as its background color.
  400.  *
  401.  * Results:
  402.  *    A standard Tcl result code.  If an error occurs, then
  403.  *    an error message is left in interp->result.
  404.  *
  405.  * Side effects:
  406.  *    Configuration information, such as colors and stipple
  407.  *    patterns, may be set for itemPtr.
  408.  *
  409.  *--------------------------------------------------------------
  410.  */
  411.  
  412. static int
  413. ConfigureLine(interp, canvas, itemPtr, argc, argv, flags)
  414.     Tcl_Interp *interp;        /* Used for error reporting. */
  415.     Tk_Canvas canvas;        /* Canvas containing itemPtr. */
  416.     Tk_Item *itemPtr;        /* Line item to reconfigure. */
  417.     int argc;            /* Number of elements in argv.  */
  418.     char **argv;        /* Arguments describing things to configure. */
  419.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  420. {
  421.     LineItem *linePtr = (LineItem *) itemPtr;
  422.     XGCValues gcValues;
  423.     GC newGC, arrowGC;
  424.     unsigned long mask;
  425.     Tk_Window tkwin;
  426.  
  427.     tkwin = Tk_CanvasTkwin(canvas);
  428.     if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, argv,
  429.         (char *) linePtr, flags) != TCL_OK) {
  430.     return TCL_ERROR;
  431.     }
  432.  
  433.     /*
  434.      * A few of the options require additional processing, such as
  435.      * graphics contexts.
  436.      */
  437.  
  438.     if (linePtr->fg == NULL) {
  439.     newGC = arrowGC = None;
  440.     } else {
  441.     gcValues.foreground = linePtr->fg->pixel;
  442.     gcValues.join_style = linePtr->joinStyle;
  443.     if (linePtr->width < 0) {
  444.         linePtr->width = 1;
  445.     }
  446.     gcValues.line_width = linePtr->width;
  447.     mask = GCForeground|GCJoinStyle|GCLineWidth;
  448.     if (linePtr->fillStipple != None) {
  449.         gcValues.stipple = linePtr->fillStipple;
  450.         gcValues.fill_style = FillStippled;
  451.         mask |= GCStipple|GCFillStyle;
  452.     }
  453.     if (linePtr->arrow == noneUid) {
  454.         gcValues.cap_style = linePtr->capStyle;
  455.         mask |= GCCapStyle;
  456.     }
  457.     newGC = Tk_GetGC(tkwin, mask, &gcValues);
  458.     gcValues.line_width = 0;
  459.     arrowGC = Tk_GetGC(tkwin, mask, &gcValues);
  460.     }
  461.     if (linePtr->gc != None) {
  462.     Tk_FreeGC(Tk_Display(tkwin), linePtr->gc);
  463.     }
  464.     if (linePtr->arrowGC != None) {
  465.     Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC);
  466.     }
  467.     linePtr->gc = newGC;
  468.     linePtr->arrowGC = arrowGC;
  469.  
  470.     /*
  471.      * Keep spline parameters within reasonable limits.
  472.      */
  473.  
  474.     if (linePtr->splineSteps < 1) {
  475.     linePtr->splineSteps = 1;
  476.     } else if (linePtr->splineSteps > 100) {
  477.     linePtr->splineSteps = 100;
  478.     }
  479.  
  480.     /*
  481.      * Setup arrowheads, if needed.  If arrowheads are turned off,
  482.      * restore the line's endpoints (they were shortened when the
  483.      * arrowheads were added).
  484.      */
  485.  
  486.     if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != firstUid)
  487.         && (linePtr->arrow != bothUid)) {
  488.     linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
  489.     linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
  490.     ckfree((char *) linePtr->firstArrowPtr);
  491.     linePtr->firstArrowPtr = NULL;
  492.     }
  493.     if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != lastUid)
  494.         && (linePtr->arrow != bothUid)) {
  495.     int i;
  496.  
  497.     i = 2*(linePtr->numPoints-1);
  498.     linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
  499.     linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
  500.     ckfree((char *) linePtr->lastArrowPtr);
  501.     linePtr->lastArrowPtr = NULL;
  502.     }
  503.     if (linePtr->arrow != noneUid) {
  504.     if ((linePtr->arrow != firstUid) && (linePtr->arrow != lastUid)
  505.         && (linePtr->arrow != bothUid)) {
  506.         Tcl_AppendResult(interp, "bad arrow spec \"",
  507.             linePtr->arrow, "\": must be none, first, last, or both",
  508.             (char *) NULL);
  509.         linePtr->arrow = noneUid;
  510.         return TCL_ERROR;
  511.     }
  512.     ConfigureArrows(canvas, linePtr);
  513.     }
  514.  
  515.     /*
  516.      * Recompute bounding box for line.
  517.      */
  518.  
  519.     ComputeLineBbox(canvas, linePtr);
  520.  
  521.     return TCL_OK;
  522. }
  523.  
  524. /*
  525.  *--------------------------------------------------------------
  526.  *
  527.  * DeleteLine --
  528.  *
  529.  *    This procedure is called to clean up the data structure
  530.  *    associated with a line item.
  531.  *
  532.  * Results:
  533.  *    None.
  534.  *
  535.  * Side effects:
  536.  *    Resources associated with itemPtr are released.
  537.  *
  538.  *--------------------------------------------------------------
  539.  */
  540.  
  541. static void
  542. DeleteLine(canvas, itemPtr, display)
  543.     Tk_Canvas canvas;            /* Info about overall canvas widget. */
  544.     Tk_Item *itemPtr;            /* Item that is being deleted. */
  545.     Display *display;            /* Display containing window for
  546.                      * canvas. */
  547. {
  548.     LineItem *linePtr = (LineItem *) itemPtr;
  549.  
  550.     if (linePtr->coordPtr != NULL) {
  551.     ckfree((char *) linePtr->coordPtr);
  552.     }
  553.     if (linePtr->fg != NULL) {
  554.     Tk_FreeColor(linePtr->fg);
  555.     }
  556.     if (linePtr->fillStipple != None) {
  557.     Tk_FreeBitmap(display, linePtr->fillStipple);
  558.     }
  559.     if (linePtr->gc != None) {
  560.     Tk_FreeGC(display, linePtr->gc);
  561.     }
  562.     if (linePtr->arrowGC != None) {
  563.     Tk_FreeGC(display, linePtr->arrowGC);
  564.     }
  565.     if (linePtr->firstArrowPtr != NULL) {
  566.     ckfree((char *) linePtr->firstArrowPtr);
  567.     }
  568.     if (linePtr->lastArrowPtr != NULL) {
  569.     ckfree((char *) linePtr->lastArrowPtr);
  570.     }
  571. }
  572.  
  573. /*
  574.  *--------------------------------------------------------------
  575.  *
  576.  * ComputeLineBbox --
  577.  *
  578.  *    This procedure is invoked to compute the bounding box of
  579.  *    all the pixels that may be drawn as part of a line.
  580.  *
  581.  * Results:
  582.  *    None.
  583.  *
  584.  * Side effects:
  585.  *    The fields x1, y1, x2, and y2 are updated in the header
  586.  *    for itemPtr.
  587.  *
  588.  *--------------------------------------------------------------
  589.  */
  590.  
  591. static void
  592. ComputeLineBbox(canvas, linePtr)
  593.     Tk_Canvas canvas;            /* Canvas that contains item. */
  594.     LineItem *linePtr;            /* Item whose bbos is to be
  595.                      * recomputed. */
  596. {
  597.     double *coordPtr;
  598.     int i;
  599.  
  600.     coordPtr = linePtr->coordPtr;
  601.     linePtr->header.x1 = linePtr->header.x2 = *coordPtr;
  602.     linePtr->header.y1 = linePtr->header.y2 = coordPtr[1];
  603.  
  604.     /*
  605.      * Compute the bounding box of all the points in the line,
  606.      * then expand in all directions by the line's width to take
  607.      * care of butting or rounded corners and projecting or
  608.      * rounded caps.  This expansion is an overestimate (worst-case
  609.      * is square root of two over two) but it's simple.  Don't do
  610.      * anything special for curves.  This causes an additional
  611.      * overestimate in the bounding box, but is faster.
  612.      */
  613.  
  614.     for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;
  615.         i++, coordPtr += 2) {
  616.     TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  617.     }
  618.     linePtr->header.x1 -= linePtr->width;
  619.     linePtr->header.x2 += linePtr->width;
  620.     linePtr->header.y1 -= linePtr->width;
  621.     linePtr->header.y2 += linePtr->width;
  622.  
  623.     /*
  624.      * For mitered lines, make a second pass through all the points.
  625.      * Compute the locations of the two miter vertex points and add
  626.      * those into the bounding box.
  627.      */
  628.  
  629.     if (linePtr->joinStyle == JoinMiter) {
  630.     for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;
  631.         i--, coordPtr += 2) {
  632.         double miter[4];
  633.         int j;
  634.     
  635.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  636.             (double) linePtr->width, miter, miter+2)) {
  637.         for (j = 0; j < 4; j += 2) {
  638.             TkIncludePoint((Tk_Item *) linePtr, miter+j);
  639.         }
  640.         }
  641.     }
  642.     }
  643.  
  644.     /*
  645.      * Add in the sizes of arrowheads, if any.
  646.      */
  647.  
  648.     if (linePtr->arrow != noneUid) {
  649.     if (linePtr->arrow != lastUid) {
  650.         for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  651.             i++, coordPtr += 2) {
  652.         TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  653.         }
  654.     }
  655.     if (linePtr->arrow != firstUid) {
  656.         for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  657.             i++, coordPtr += 2) {
  658.         TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  659.         }
  660.     }
  661.     }
  662.  
  663.     /*
  664.      * Add one more pixel of fudge factor just to be safe (e.g.
  665.      * X may round differently than we do).
  666.      */
  667.  
  668.     linePtr->header.x1 -= 1;
  669.     linePtr->header.x2 += 1;
  670.     linePtr->header.y1 -= 1;
  671.     linePtr->header.y2 += 1;
  672. }
  673.  
  674. /*
  675.  *--------------------------------------------------------------
  676.  *
  677.  * DisplayLine --
  678.  *
  679.  *    This procedure is invoked to draw a line item in a given
  680.  *    drawable.
  681.  *
  682.  * Results:
  683.  *    None.
  684.  *
  685.  * Side effects:
  686.  *    ItemPtr is drawn in drawable using the transformation
  687.  *    information in canvas.
  688.  *
  689.  *--------------------------------------------------------------
  690.  */
  691.  
  692. static void
  693. DisplayLine(canvas, itemPtr, display, drawable, x, y, width, height)
  694.     Tk_Canvas canvas;            /* Canvas that contains item. */
  695.     Tk_Item *itemPtr;            /* Item to be displayed. */
  696.     Display *display;            /* Display on which to draw item. */
  697.     Drawable drawable;            /* Pixmap or window in which to draw
  698.                      * item. */
  699.     int x, y, width, height;        /* Describes region of canvas that
  700.                      * must be redisplayed (not used). */
  701. {
  702.     LineItem *linePtr = (LineItem *) itemPtr;
  703.     XPoint staticPoints[MAX_STATIC_POINTS];
  704.     XPoint *pointPtr;
  705.     XPoint *pPtr;
  706.     double *coordPtr;
  707.     int i, numPoints;
  708.  
  709.     if (linePtr->gc == None) {
  710.     return;
  711.     }
  712.  
  713.     /*
  714.      * Build up an array of points in screen coordinates.  Use a
  715.      * static array unless the line has an enormous number of points;
  716.      * in this case, dynamically allocate an array.  For smoothed lines,
  717.      * generate the curve points on each redisplay.
  718.      */
  719.  
  720.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  721.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  722.     } else {
  723.     numPoints = linePtr->numPoints;
  724.     }
  725.  
  726.     if (numPoints <= MAX_STATIC_POINTS) {
  727.     pointPtr = staticPoints;
  728.     } else {
  729.     pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));
  730.     }
  731.  
  732.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  733.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  734.         linePtr->numPoints, linePtr->splineSteps, pointPtr,
  735.         (double *) NULL);
  736.     } else {
  737.     for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr;
  738.         i < linePtr->numPoints;  i += 1, coordPtr += 2, pPtr++) {
  739.         Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1],
  740.             &pPtr->x, &pPtr->y);
  741.     }
  742.     }
  743.  
  744.     /*
  745.      * Display line, the free up line storage if it was dynamically
  746.      * allocated.  If we're stippling, then modify the stipple offset
  747.      * in the GC.  Be sure to reset the offset when done, since the
  748.      * GC is supposed to be read-only.
  749.      */
  750.  
  751.     if (linePtr->fillStipple != None) {
  752.     Tk_CanvasSetStippleOrigin(canvas, linePtr->gc);
  753.     Tk_CanvasSetStippleOrigin(canvas, linePtr->arrowGC);
  754.     }
  755.     XDrawLines(display, drawable, linePtr->gc, pointPtr, numPoints,
  756.         CoordModeOrigin);
  757.     if (pointPtr != staticPoints) {
  758.     ckfree((char *) pointPtr);
  759.     }
  760.  
  761.     /*
  762.      * Display arrowheads, if they are wanted.
  763.      */
  764.  
  765.     if (linePtr->firstArrowPtr != NULL) {
  766.     TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW,
  767.         display, drawable, linePtr->gc, NULL);
  768.     }
  769.     if (linePtr->lastArrowPtr != NULL) {
  770.     TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW,
  771.         display, drawable, linePtr->gc, NULL);
  772.     }
  773.     if (linePtr->fillStipple != None) {
  774.     XSetTSOrigin(display, linePtr->gc, 0, 0);
  775.     XSetTSOrigin(display, linePtr->arrowGC, 0, 0);
  776.     }
  777. }
  778.  
  779. /*
  780.  *--------------------------------------------------------------
  781.  *
  782.  * LineToPoint --
  783.  *
  784.  *    Computes the distance from a given point to a given
  785.  *    line, in canvas units.
  786.  *
  787.  * Results:
  788.  *    The return value is 0 if the point whose x and y coordinates
  789.  *    are pointPtr[0] and pointPtr[1] is inside the line.  If the
  790.  *    point isn't inside the line then the return value is the
  791.  *    distance from the point to the line.
  792.  *
  793.  * Side effects:
  794.  *    None.
  795.  *
  796.  *--------------------------------------------------------------
  797.  */
  798.  
  799.     /* ARGSUSED */
  800. static double
  801. LineToPoint(canvas, itemPtr, pointPtr)
  802.     Tk_Canvas canvas;        /* Canvas containing item. */
  803.     Tk_Item *itemPtr;        /* Item to check against point. */
  804.     double *pointPtr;        /* Pointer to x and y coordinates. */
  805. {
  806.     LineItem *linePtr = (LineItem *) itemPtr;
  807.     double *coordPtr, *linePoints;
  808.     double staticSpace[2*MAX_STATIC_POINTS];
  809.     double poly[10];
  810.     double bestDist, dist;
  811.     int numPoints, count;
  812.     int changedMiterToBevel;    /* Non-zero means that a mitered corner
  813.                  * had to be treated as beveled after all
  814.                  * because the angle was < 11 degrees. */
  815.  
  816.     bestDist = 1.0e40;
  817.  
  818.     /*
  819.      * Handle smoothed lines by generating an expanded set of points
  820.      * against which to do the check.
  821.      */
  822.  
  823.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  824.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  825.     if (numPoints <= MAX_STATIC_POINTS) {
  826.         linePoints = staticSpace;
  827.     } else {
  828.         linePoints = (double *) ckalloc((unsigned)
  829.             (2*numPoints*sizeof(double)));
  830.     }
  831.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  832.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  833.         linePoints);
  834.     } else {
  835.     numPoints = linePtr->numPoints;
  836.     linePoints = linePtr->coordPtr;
  837.     }
  838.  
  839.     /*
  840.      * The overall idea is to iterate through all of the edges of
  841.      * the line, computing a polygon for each edge and testing the
  842.      * point against that polygon.  In addition, there are additional
  843.      * tests to deal with rounded joints and caps.
  844.      */
  845.  
  846.     changedMiterToBevel = 0;
  847.     for (count = numPoints, coordPtr = linePoints; count >= 2;
  848.         count--, coordPtr += 2) {
  849.  
  850.     /*
  851.      * If rounding is done around the first point then compute
  852.      * the distance between the point and the point.
  853.      */
  854.  
  855.     if (((linePtr->capStyle == CapRound) && (count == numPoints))
  856.         || ((linePtr->joinStyle == JoinRound)
  857.             && (count != numPoints))) {
  858.         dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  859.             - linePtr->width/2.0;
  860.         if (dist <= 0.0) {
  861.         bestDist = 0.0;
  862.         goto done;
  863.         } else if (dist < bestDist) {
  864.         bestDist = dist;
  865.         }
  866.     }
  867.  
  868.     /*
  869.      * Compute the polygonal shape corresponding to this edge,
  870.      * consisting of two points for the first point of the edge
  871.      * and two points for the last point of the edge.
  872.      */
  873.  
  874.     if (count == numPoints) {
  875.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width,
  876.             linePtr->capStyle == CapProjecting, poly, poly+2);
  877.     } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
  878.         poly[0] = poly[6];
  879.         poly[1] = poly[7];
  880.         poly[2] = poly[4];
  881.         poly[3] = poly[5];
  882.     } else {
  883.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0,
  884.             poly, poly+2);
  885.  
  886.         /*
  887.          * If this line uses beveled joints, then check the distance
  888.          * to a polygon comprising the last two points of the previous
  889.          * polygon and the first two from this polygon;  this checks
  890.          * the wedges that fill the mitered joint.
  891.          */
  892.  
  893.         if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
  894.         poly[8] = poly[0];
  895.         poly[9] = poly[1];
  896.         dist = TkPolygonToPoint(poly, 5, pointPtr);
  897.         if (dist <= 0.0) {
  898.             bestDist = 0.0;
  899.             goto done;
  900.         } else if (dist < bestDist) {
  901.             bestDist = dist;
  902.         }
  903.         changedMiterToBevel = 0;
  904.         }
  905.     }
  906.     if (count == 2) {
  907.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  908.             linePtr->capStyle == CapProjecting, poly+4, poly+6);
  909.     } else if (linePtr->joinStyle == JoinMiter) {
  910.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  911.             (double) linePtr->width, poly+4, poly+6) == 0) {
  912.         changedMiterToBevel = 1;
  913.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  914.             0, poly+4, poly+6);
  915.         }
  916.     } else {
  917.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0,
  918.             poly+4, poly+6);
  919.     }
  920.     poly[8] = poly[0];
  921.     poly[9] = poly[1];
  922.     dist = TkPolygonToPoint(poly, 5, pointPtr);
  923.     if (dist <= 0.0) {
  924.         bestDist = 0.0;
  925.         goto done;
  926.     } else if (dist < bestDist) {
  927.         bestDist = dist;
  928.     }
  929.     }
  930.  
  931.     /*
  932.      * If caps are rounded, check the distance to the cap around the
  933.      * final end point of the line.
  934.      */
  935.  
  936.     if (linePtr->capStyle == CapRound) {
  937.     dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  938.         - linePtr->width/2.0;
  939.     if (dist <= 0.0) {
  940.         bestDist = 0.0;
  941.         goto done;
  942.     } else if (dist < bestDist) {
  943.         bestDist = dist;
  944.     }
  945.     }
  946.  
  947.     /*
  948.      * If there are arrowheads, check the distance to the arrowheads.
  949.      */
  950.  
  951.     if (linePtr->arrow != noneUid) {
  952.     if (linePtr->arrow != lastUid) {
  953.         dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,
  954.             pointPtr);
  955.         if (dist <= 0.0) {
  956.         bestDist = 0.0;
  957.         goto done;
  958.         } else if (dist < bestDist) {
  959.         bestDist = dist;
  960.         }
  961.     }
  962.     if (linePtr->arrow != firstUid) {
  963.         dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,
  964.             pointPtr);
  965.         if (dist <= 0.0) {
  966.         bestDist = 0.0;
  967.         goto done;
  968.         } else if (dist < bestDist) {
  969.         bestDist = dist;
  970.         }
  971.     }
  972.     }
  973.  
  974.     done:
  975.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  976.     ckfree((char *) linePoints);
  977.     }
  978.     return bestDist;
  979. }
  980.  
  981. /*
  982.  *--------------------------------------------------------------
  983.  *
  984.  * LineToArea --
  985.  *
  986.  *    This procedure is called to determine whether an item
  987.  *    lies entirely inside, entirely outside, or overlapping
  988.  *    a given rectangular area.
  989.  *
  990.  * Results:
  991.  *    -1 is returned if the item is entirely outside the
  992.  *    area, 0 if it overlaps, and 1 if it is entirely
  993.  *    inside the given area.
  994.  *
  995.  * Side effects:
  996.  *    None.
  997.  *
  998.  *--------------------------------------------------------------
  999.  */
  1000.  
  1001.     /* ARGSUSED */
  1002. static int
  1003. LineToArea(canvas, itemPtr, rectPtr)
  1004.     Tk_Canvas canvas;        /* Canvas containing item. */
  1005.     Tk_Item *itemPtr;        /* Item to check against line. */
  1006.     double *rectPtr;
  1007. {
  1008.     LineItem *linePtr = (LineItem *) itemPtr;
  1009.     double staticSpace[2*MAX_STATIC_POINTS];
  1010.     double *linePoints;
  1011.     double radius;
  1012.     int numPoints, result;
  1013.  
  1014.     radius = linePtr->width/2.0;
  1015.  
  1016.     /*
  1017.      * Handle smoothed lines by generating an expanded set of points
  1018.      * against which to do the check.
  1019.      */
  1020.  
  1021.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  1022.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  1023.     if (numPoints <= MAX_STATIC_POINTS) {
  1024.         linePoints = staticSpace;
  1025.     } else {
  1026.         linePoints = (double *) ckalloc((unsigned)
  1027.             (2*numPoints*sizeof(double)));
  1028.     }
  1029.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  1030.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  1031.         linePoints);
  1032.     } else {
  1033.     numPoints = linePtr->numPoints;
  1034.     linePoints = linePtr->coordPtr;
  1035.     }
  1036.  
  1037.     /*
  1038.      * Check the segments of the line.
  1039.      */
  1040.  
  1041.     result = TkThickPolyLineToArea(linePoints, numPoints, 
  1042.         (double) linePtr->width, linePtr->capStyle, linePtr->joinStyle,
  1043.         rectPtr);
  1044.     if (result == 0) {
  1045.     goto done;
  1046.     }
  1047.  
  1048.     /*
  1049.      * Check arrowheads, if any.
  1050.      */
  1051.  
  1052.     if (linePtr->arrow != noneUid) {
  1053.     if (linePtr->arrow != lastUid) {
  1054.         if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,
  1055.             rectPtr) != result) {
  1056.         result = 0;
  1057.         goto done;
  1058.         }
  1059.     }
  1060.     if (linePtr->arrow != firstUid) {
  1061.         if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,
  1062.             rectPtr) != result) {
  1063.         result = 0;
  1064.         goto done;
  1065.         }
  1066.     }
  1067.     }
  1068.  
  1069.     done:
  1070.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  1071.     ckfree((char *) linePoints);
  1072.     }
  1073.     return result;
  1074. }
  1075.  
  1076. /*
  1077.  *--------------------------------------------------------------
  1078.  *
  1079.  * ScaleLine --
  1080.  *
  1081.  *    This procedure is invoked to rescale a line item.
  1082.  *
  1083.  * Results:
  1084.  *    None.
  1085.  *
  1086.  * Side effects:
  1087.  *    The line referred to by itemPtr is rescaled so that the
  1088.  *    following transformation is applied to all point
  1089.  *    coordinates:
  1090.  *        x' = originX + scaleX*(x-originX)
  1091.  *        y' = originY + scaleY*(y-originY)
  1092.  *
  1093.  *--------------------------------------------------------------
  1094.  */
  1095.  
  1096. static void
  1097. ScaleLine(canvas, itemPtr, originX, originY, scaleX, scaleY)
  1098.     Tk_Canvas canvas;            /* Canvas containing line. */
  1099.     Tk_Item *itemPtr;            /* Line to be scaled. */
  1100.     double originX, originY;        /* Origin about which to scale rect. */
  1101.     double scaleX;            /* Amount to scale in X direction. */
  1102.     double scaleY;            /* Amount to scale in Y direction. */
  1103. {
  1104.     LineItem *linePtr = (LineItem *) itemPtr;
  1105.     double *coordPtr;
  1106.     int i;
  1107.  
  1108.     /*
  1109.      * Delete any arrowheads before scaling all the points (so that
  1110.      * the end-points of the line get restored).
  1111.      */
  1112.  
  1113.     if (linePtr->firstArrowPtr != NULL) {
  1114.     linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
  1115.     linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
  1116.     ckfree((char *) linePtr->firstArrowPtr);
  1117.     linePtr->firstArrowPtr = NULL;
  1118.     }
  1119.     if (linePtr->lastArrowPtr != NULL) {
  1120.     int i;
  1121.  
  1122.     i = 2*(linePtr->numPoints-1);
  1123.     linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
  1124.     linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
  1125.     ckfree((char *) linePtr->lastArrowPtr);
  1126.     linePtr->lastArrowPtr = NULL;
  1127.     }
  1128.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1129.         i++, coordPtr += 2) {
  1130.     coordPtr[0] = originX + scaleX*(*coordPtr - originX);
  1131.     coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
  1132.     }
  1133.     if (linePtr->arrow != noneUid) {
  1134.     ConfigureArrows(canvas, linePtr);
  1135.     }
  1136.     ComputeLineBbox(canvas, linePtr);
  1137. }
  1138.  
  1139. /*
  1140.  *--------------------------------------------------------------
  1141.  *
  1142.  * TranslateLine --
  1143.  *
  1144.  *    This procedure is called to move a line by a given amount.
  1145.  *
  1146.  * Results:
  1147.  *    None.
  1148.  *
  1149.  * Side effects:
  1150.  *    The position of the line is offset by (xDelta, yDelta), and
  1151.  *    the bounding box is updated in the generic part of the item
  1152.  *    structure.
  1153.  *
  1154.  *--------------------------------------------------------------
  1155.  */
  1156.  
  1157. static void
  1158. TranslateLine(canvas, itemPtr, deltaX, deltaY)
  1159.     Tk_Canvas canvas;            /* Canvas containing item. */
  1160.     Tk_Item *itemPtr;            /* Item that is being moved. */
  1161.     double deltaX, deltaY;        /* Amount by which item is to be
  1162.                      * moved. */
  1163. {
  1164.     LineItem *linePtr = (LineItem *) itemPtr;
  1165.     double *coordPtr;
  1166.     int i;
  1167.  
  1168.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1169.         i++, coordPtr += 2) {
  1170.     coordPtr[0] += deltaX;
  1171.     coordPtr[1] += deltaY;
  1172.     }
  1173.     if (linePtr->firstArrowPtr != NULL) {
  1174.     for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  1175.         i++, coordPtr += 2) {
  1176.         coordPtr[0] += deltaX;
  1177.         coordPtr[1] += deltaY;
  1178.     }
  1179.     }
  1180.     if (linePtr->lastArrowPtr != NULL) {
  1181.     for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  1182.         i++, coordPtr += 2) {
  1183.         coordPtr[0] += deltaX;
  1184.         coordPtr[1] += deltaY;
  1185.     }
  1186.     }
  1187.     ComputeLineBbox(canvas, linePtr);
  1188. }
  1189.  
  1190. /*
  1191.  *--------------------------------------------------------------
  1192.  *
  1193.  * ParseArrowShape --
  1194.  *
  1195.  *    This procedure is called back during option parsing to
  1196.  *    parse arrow shape information.
  1197.  *
  1198.  * Results:
  1199.  *    The return value is a standard Tcl result:  TCL_OK means
  1200.  *    that the arrow shape information was parsed ok, and
  1201.  *    TCL_ERROR means it couldn't be parsed.
  1202.  *
  1203.  * Side effects:
  1204.  *    Arrow information in recordPtr is updated.
  1205.  *
  1206.  *--------------------------------------------------------------
  1207.  */
  1208.  
  1209.     /* ARGSUSED */
  1210. static int
  1211. ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset)
  1212.     ClientData clientData;    /* Not used. */
  1213.     Tcl_Interp *interp;        /* Used for error reporting. */
  1214.     Tk_Window tkwin;        /* Not used. */
  1215.     char *value;        /* Textual specification of arrow shape. */
  1216.     char *recordPtr;        /* Pointer to item record in which to
  1217.                  * store arrow information. */
  1218.     int offset;            /* Offset of shape information in widget
  1219.                  * record. */
  1220. {
  1221.     LineItem *linePtr = (LineItem *) recordPtr;
  1222.     double a, b, c;
  1223.     int argc;
  1224.     char **argv = NULL;
  1225.  
  1226.     if (offset != Tk_Offset(LineItem, arrowShapeA)) {
  1227.     panic("ParseArrowShape received bogus offset");
  1228.     }
  1229.  
  1230.     if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
  1231.     syntaxError:
  1232.     Tcl_ResetResult(interp);
  1233.     Tcl_AppendResult(interp, "bad arrow shape \"", value,
  1234.         "\": must be list with three numbers", (char *) NULL);
  1235.     if (argv != NULL) {
  1236.         ckfree((char *) argv);
  1237.     }
  1238.     return TCL_ERROR;
  1239.     }
  1240.     if (argc != 3) {
  1241.     goto syntaxError;
  1242.     }
  1243.     if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK)
  1244.         || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[1], &b)
  1245.         != TCL_OK)
  1246.         || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[2], &c)
  1247.         != TCL_OK)) {
  1248.     goto syntaxError;
  1249.     }
  1250.     linePtr->arrowShapeA = a;
  1251.     linePtr->arrowShapeB = b;
  1252.     linePtr->arrowShapeC = c;
  1253.     ckfree((char *) argv);
  1254.     return TCL_OK;
  1255. }
  1256.  
  1257. /*
  1258.  *--------------------------------------------------------------
  1259.  *
  1260.  * PrintArrowShape --
  1261.  *
  1262.  *    This procedure is a callback invoked by the configuration
  1263.  *    code to return a printable value describing an arrow shape.
  1264.  *
  1265.  * Results:
  1266.  *    None.
  1267.  *
  1268.  * Side effects:
  1269.  *    None.
  1270.  *
  1271.  *--------------------------------------------------------------
  1272.  */
  1273.  
  1274.     /* ARGSUSED */
  1275. static char *
  1276. PrintArrowShape(clientData, tkwin, recordPtr, offset, freeProcPtr)
  1277.     ClientData clientData;    /* Not used. */
  1278.     Tk_Window tkwin;        /* Window associated with linePtr's widget. */
  1279.     char *recordPtr;        /* Pointer to item record containing current
  1280.                  * shape information. */
  1281.     int offset;            /* Offset of arrow information in record. */
  1282.     Tcl_FreeProc **freeProcPtr;    /* Store address of procedure to call to
  1283.                  * free string here. */
  1284. {
  1285.     LineItem *linePtr = (LineItem *) recordPtr;
  1286.     char *buffer;
  1287.  
  1288.     buffer = (char *) ckalloc(120);
  1289.     sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA,
  1290.         linePtr->arrowShapeB, linePtr->arrowShapeC);
  1291.     *freeProcPtr = (Tcl_FreeProc *) free;
  1292.     return buffer;
  1293. }
  1294.  
  1295. /*
  1296.  *--------------------------------------------------------------
  1297.  *
  1298.  * ConfigureArrows --
  1299.  *
  1300.  *    If arrowheads have been requested for a line, this
  1301.  *    procedure makes arrangements for the arrowheads.
  1302.  *
  1303.  * Results:
  1304.  *    Always returns TCL_OK.
  1305.  *
  1306.  * Side effects:
  1307.  *    Information in linePtr is set up for one or two arrowheads.
  1308.  *    the firstArrowPtr and lastArrowPtr polygons are allocated
  1309.  *    and initialized, if need be, and the end points of the line
  1310.  *    are adjusted so that a thick line doesn't stick out past
  1311.  *    the arrowheads.
  1312.  *
  1313.  *--------------------------------------------------------------
  1314.  */
  1315.  
  1316.     /* ARGSUSED */
  1317. static int
  1318. ConfigureArrows(canvas, linePtr)
  1319.     Tk_Canvas canvas;            /* Canvas in which arrows will be
  1320.                      * displayed (interp and tkwin
  1321.                      * fields are needed). */
  1322.     LineItem *linePtr;            /* Item to configure for arrows. */
  1323. {
  1324.     double *poly, *coordPtr;
  1325.     double dx, dy, length, sinTheta, cosTheta, temp;
  1326.     double fracHeight;            /* Line width as fraction of
  1327.                      * arrowhead width. */
  1328.     double backup;            /* Distance to backup end points
  1329.                      * so the line ends in the middle
  1330.                      * of the arrowhead. */
  1331.     double vertX, vertY;        /* Position of arrowhead vertex. */
  1332.     double shapeA, shapeB, shapeC;    /* Adjusted coordinates (see
  1333.                      * explanation below). */
  1334.  
  1335.     /*
  1336.      * The code below makes a tiny increase in the shape parameters
  1337.      * for the line.  This is a bit of a hack, but it seems to result
  1338.      * in displays that more closely approximate the specified parameters.
  1339.      * Without the adjustment, the arrows come out smaller than expected.
  1340.      */
  1341.  
  1342.     shapeA = linePtr->arrowShapeA + 0.001;
  1343.     shapeB = linePtr->arrowShapeB + 0.001;
  1344.     shapeC = linePtr->arrowShapeC + linePtr->width/2.0 + 0.001;
  1345.  
  1346.     /*
  1347.      * If there's an arrowhead on the first point of the line, compute
  1348.      * its polygon and adjust the first point of the line so that the
  1349.      * line doesn't stick out past the leading edge of the arrowhead.
  1350.      */
  1351.  
  1352.     fracHeight = (linePtr->width/2.0)/shapeC;
  1353.     backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0;
  1354.     if (linePtr->arrow != lastUid) {
  1355.     poly = linePtr->firstArrowPtr;
  1356.     if (poly == NULL) {
  1357.         poly = (double *) ckalloc((unsigned)
  1358.             (2*PTS_IN_ARROW*sizeof(double)));
  1359.         poly[0] = poly[10] = linePtr->coordPtr[0];
  1360.         poly[1] = poly[11] = linePtr->coordPtr[1];
  1361.         linePtr->firstArrowPtr = poly;
  1362.     }
  1363.     dx = poly[0] - linePtr->coordPtr[2];
  1364.     dy = poly[1] - linePtr->coordPtr[3];
  1365.     length = hypot(dx, dy);
  1366.     if (length == 0) {
  1367.         sinTheta = cosTheta = 0.0;
  1368.     } else {
  1369.         sinTheta = dy/length;
  1370.         cosTheta = dx/length;
  1371.     }
  1372.     vertX = poly[0] - shapeA*cosTheta;
  1373.     vertY = poly[1] - shapeA*sinTheta;
  1374.     temp = shapeC*sinTheta;
  1375.     poly[2] = poly[0] - shapeB*cosTheta + temp;
  1376.     poly[8] = poly[2] - 2*temp;
  1377.     temp = shapeC*cosTheta;
  1378.     poly[3] = poly[1] - shapeB*sinTheta - temp;
  1379.     poly[9] = poly[3] + 2*temp;
  1380.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1381.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1382.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1383.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1384.  
  1385.     /*
  1386.      * Polygon done.  Now move the first point towards the second so
  1387.      * that the corners at the end of the line are inside the
  1388.      * arrowhead.
  1389.      */
  1390.  
  1391.     linePtr->coordPtr[0] = poly[0] - backup*cosTheta;
  1392.     linePtr->coordPtr[1] = poly[1] - backup*sinTheta;
  1393.     }
  1394.  
  1395.     /*
  1396.      * Similar arrowhead calculation for the last point of the line.
  1397.      */
  1398.  
  1399.     if (linePtr->arrow != firstUid) {
  1400.     coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);
  1401.     poly = linePtr->lastArrowPtr;
  1402.     if (poly == NULL) {
  1403.         poly = (double *) ckalloc((unsigned)
  1404.             (2*PTS_IN_ARROW*sizeof(double)));
  1405.         poly[0] = poly[10] = coordPtr[2];
  1406.         poly[1] = poly[11] = coordPtr[3];
  1407.         linePtr->lastArrowPtr = poly;
  1408.     }
  1409.     dx = poly[0] - coordPtr[0];
  1410.     dy = poly[1] - coordPtr[1];
  1411.     length = hypot(dx, dy);
  1412.     if (length == 0) {
  1413.         sinTheta = cosTheta = 0.0;
  1414.     } else {
  1415.         sinTheta = dy/length;
  1416.         cosTheta = dx/length;
  1417.     }
  1418.     vertX = poly[0] - shapeA*cosTheta;
  1419.     vertY = poly[1] - shapeA*sinTheta;
  1420.     temp = shapeC*sinTheta;
  1421.     poly[2] = poly[0] - shapeB*cosTheta + temp;
  1422.     poly[8] = poly[2] - 2*temp;
  1423.     temp = shapeC*cosTheta;
  1424.     poly[3] = poly[1] - shapeB*sinTheta - temp;
  1425.     poly[9] = poly[3] + 2*temp;
  1426.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1427.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1428.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1429.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1430.     coordPtr[2] = poly[0] - backup*cosTheta;
  1431.     coordPtr[3] = poly[1] - backup*sinTheta;
  1432.     }
  1433.  
  1434.     return TCL_OK;
  1435. }
  1436.  
  1437. /*
  1438.  *--------------------------------------------------------------
  1439.  *
  1440.  * LineToPostscript --
  1441.  *
  1442.  *    This procedure is called to generate Postscript for
  1443.  *    line items.
  1444.  *
  1445.  * Results:
  1446.  *    The return value is a standard Tcl result.  If an error
  1447.  *    occurs in generating Postscript then an error message is
  1448.  *    left in interp->result, replacing whatever used
  1449.  *    to be there.  If no error occurs, then Postscript for the
  1450.  *    item is appended to the result.
  1451.  *
  1452.  * Side effects:
  1453.  *    None.
  1454.  *
  1455.  *--------------------------------------------------------------
  1456.  */
  1457.  
  1458. static int
  1459. LineToPostscript(interp, canvas, itemPtr, prepass)
  1460.     Tcl_Interp *interp;            /* Leave Postscript or error message
  1461.                      * here. */
  1462.     Tk_Canvas canvas;            /* Information about overall canvas. */
  1463.     Tk_Item *itemPtr;            /* Item for which Postscript is
  1464.                      * wanted. */
  1465.     int prepass;            /* 1 means this is a prepass to
  1466.                      * collect font information;  0 means
  1467.                      * final Postscript is being created. */
  1468. {
  1469.     LineItem *linePtr = (LineItem *) itemPtr;
  1470.     char buffer[200];
  1471.     char *style;
  1472.  
  1473.     if (linePtr->fg == NULL) {
  1474.     return TCL_OK;
  1475.     }
  1476.  
  1477.     /*
  1478.      * Generate a path for the line's center-line (do this differently
  1479.      * for straight lines and smoothed lines).
  1480.      */
  1481.  
  1482.     if ((!linePtr->smooth) || (linePtr->numPoints <= 2)) {
  1483.     Tk_CanvasPsPath(interp, canvas, linePtr->coordPtr, linePtr->numPoints);
  1484.     } else {
  1485.     if (linePtr->fillStipple == None) {
  1486.         TkMakeBezierPostscript(interp, canvas, linePtr->coordPtr,
  1487.             linePtr->numPoints);
  1488.     } else {
  1489.         /*
  1490.          * Special hack: Postscript printers don't appear to be able
  1491.          * to turn a path drawn with "curveto"s into a clipping path
  1492.          * without exceeding resource limits, so TkMakeBezierPostscript
  1493.          * won't work for stippled curves.  Instead, generate all of
  1494.          * the intermediate points here and output them into the
  1495.          * Postscript file with "lineto"s instead.
  1496.          */
  1497.  
  1498.         double staticPoints[2*MAX_STATIC_POINTS];
  1499.         double *pointPtr;
  1500.         int numPoints;
  1501.  
  1502.         numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  1503.         pointPtr = staticPoints;
  1504.         if (numPoints > MAX_STATIC_POINTS) {
  1505.         pointPtr = (double *) ckalloc((unsigned)
  1506.             (numPoints * 2 * sizeof(double)));
  1507.         }
  1508.         numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  1509.             linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  1510.             pointPtr);
  1511.         Tk_CanvasPsPath(interp, canvas, pointPtr, numPoints);
  1512.         if (pointPtr != staticPoints) {
  1513.         ckfree((char *) pointPtr);
  1514.         }
  1515.     }
  1516.     }
  1517.  
  1518.     /*
  1519.      * Set other line-drawing parameters and stroke out the line.
  1520.      */
  1521.  
  1522.     sprintf(buffer, "%d setlinewidth\n", linePtr->width);
  1523.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1524.     style = "0 setlinecap\n";
  1525.     if (linePtr->capStyle == CapRound) {
  1526.     style = "1 setlinecap\n";
  1527.     } else if (linePtr->capStyle == CapProjecting) {
  1528.     style = "2 setlinecap\n";
  1529.     }
  1530.     Tcl_AppendResult(interp, style, (char *) NULL);
  1531.     style = "0 setlinejoin\n";
  1532.     if (linePtr->joinStyle == JoinRound) {
  1533.     style = "1 setlinejoin\n";
  1534.     } else if (linePtr->joinStyle == JoinBevel) {
  1535.     style = "2 setlinejoin\n";
  1536.     }
  1537.     Tcl_AppendResult(interp, style, (char *) NULL);
  1538.     if (Tk_CanvasPsColor(interp, canvas, linePtr->fg) != TCL_OK) {
  1539.     return TCL_ERROR;
  1540.     };
  1541.     if (linePtr->fillStipple != None) {
  1542.     Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
  1543.     if (Tk_CanvasPsStipple(interp, canvas, linePtr->fillStipple)
  1544.         != TCL_OK) {
  1545.         return TCL_ERROR;
  1546.     }
  1547.     } else {
  1548.     Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
  1549.     }
  1550.  
  1551.     /*
  1552.      * Output polygons for the arrowheads, if there are any.
  1553.      */
  1554.  
  1555.     if (linePtr->firstArrowPtr != NULL) {
  1556.     if (linePtr->fillStipple != None) {
  1557.         Tcl_AppendResult(interp, "grestore gsave\n",
  1558.             (char *) NULL);
  1559.     }
  1560.     if (ArrowheadPostscript(interp, canvas, linePtr,
  1561.         linePtr->firstArrowPtr) != TCL_OK) {
  1562.         return TCL_ERROR;
  1563.     }
  1564.     }
  1565.     if (linePtr->lastArrowPtr != NULL) {
  1566.     if (linePtr->fillStipple != None) {
  1567.         Tcl_AppendResult(interp, "grestore gsave\n", (char *) NULL);
  1568.     }
  1569.     if (ArrowheadPostscript(interp, canvas, linePtr,
  1570.         linePtr->lastArrowPtr) != TCL_OK) {
  1571.         return TCL_ERROR;
  1572.     }
  1573.     }
  1574.     return TCL_OK;
  1575. }
  1576.  
  1577. /*
  1578.  *--------------------------------------------------------------
  1579.  *
  1580.  * ArrowheadPostscript --
  1581.  *
  1582.  *    This procedure is called to generate Postscript for
  1583.  *    an arrowhead for a line item.
  1584.  *
  1585.  * Results:
  1586.  *    The return value is a standard Tcl result.  If an error
  1587.  *    occurs in generating Postscript then an error message is
  1588.  *    left in interp->result, replacing whatever used
  1589.  *    to be there.  If no error occurs, then Postscript for the
  1590.  *    arrowhead is appended to the result.
  1591.  *
  1592.  * Side effects:
  1593.  *    None.
  1594.  *
  1595.  *--------------------------------------------------------------
  1596.  */
  1597.  
  1598. static int
  1599. ArrowheadPostscript(interp, canvas, linePtr, arrowPtr)
  1600.     Tcl_Interp *interp;            /* Leave Postscript or error message
  1601.                      * here. */
  1602.     Tk_Canvas canvas;            /* Information about overall canvas. */
  1603.     LineItem *linePtr;            /* Line item for which Postscript is
  1604.                      * being generated. */
  1605.     double *arrowPtr;            /* Pointer to first of five points
  1606.                      * describing arrowhead polygon. */
  1607. {
  1608.     Tk_CanvasPsPath(interp, canvas, arrowPtr, PTS_IN_ARROW);
  1609.     if (linePtr->fillStipple != None) {
  1610.     Tcl_AppendResult(interp, "clip ", (char *) NULL);
  1611.     if (Tk_CanvasPsStipple(interp, canvas, linePtr->fillStipple)
  1612.         != TCL_OK) {
  1613.         return TCL_ERROR;
  1614.     }
  1615.     } else {
  1616.     Tcl_AppendResult(interp, "fill\n", (char *) NULL);
  1617.     }
  1618.     return TCL_OK;
  1619. }
  1620.